// Learn TypeScript:
//  - https://docs.cocos.com/creator/manual/en/scripting/typescript.html
// Learn Attribute:
//  - https://docs.cocos.com/creator/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
//  - https://docs.cocos.com/creator/manual/en/scripting/life-cycle-callbacks.html

import Shader from "../Shader";


const { ccclass, property } = cc._decorator;

@ccclass
export default class SpringShader extends Shader {
    @property
    private _waveTime: number = 6;

    @property
    get waveTime(): number {
        return this._waveTime;
    }

    set waveTime(time: number) {
        this._waveTime = time;
    }

    //波动幅度
    @property
    private _waveA: number = 100;

    @property
    get waveA(): number {
        return this._waveA;
    }

    set waveA(a: number) {
        this._waveA = a;
    }

    //波动频率
    @property
    private _waveW: number = 20;

    @property
    get waveW(): number {
        return this._waveW;
    }

    set waveW(w: number) {
        this._waveW = w;
    }

    //波动相位
    @property
    private _waveP: number = 0;

    @property
    get waveP(): number {
        return this._waveP;
    }

    set waveP(p: number) {
        this._waveP = p;
    }

    //衰减强度
    @property
    private _attenuation: number = 0.2;

    @property
    get waveAttenuation(): number {
        return this._attenuation;
    }

    set waveAttenuation(value: number) {
        this._attenuation = value;
    }

    //波动范围
    @property
    private _waveRange: number = 100;

    @property
    get waveRange(): number {
        return this._waveRange;
    }

    set waveRange(range: number) {
        this._waveRange = range;
    }

    @property
    private _strength: number = 0.5;

    @property
    get strength(): number {
        return this._strength;
    }

    set strength(value: number) {
        this._strength = value;
    }

    //波动中心点、偏移、时间
    private _centerOffsetTime: Float32Array = null;

    //最多同时出现四个波动点
    private _maxPosNums: number = 4;

    private _currIndex: number = 0;

    private _flaotsPerWave: number = 4;

    protected start(): void {
        this.material?.setProperty("u_waveRange", this._waveRange);
        this.material?.setProperty("u_uvRatio", cc.v2(1 / this.node.width, 1 / this.node.height));
        this._centerOffsetTime = new Float32Array(this._flaotsPerWave * this._maxPosNums);
    }

    protected update(dt: number): void {
        let offset = 0;
        for (let i = 0; i < this._maxPosNums; i++) {
            offset = i * this._flaotsPerWave;
            if (this._centerOffsetTime[offset + 3] <= 0) continue;
            this._centerOffsetTime[offset + 3] -= dt;
            this._centerOffsetTime[offset + 2] = this.wave(this._centerOffsetTime[offset + 3]);
            // cc.log("index:", i, this._centerOffsetTime[offset+2]);
        }

        this.material?.setProperty("u_waveInfo", this._centerOffsetTime);
        // this.material?.setProperty("offset", this._centerOffsetTime[0].z);
    }

    public springAt(pos: cc.Vec2) {
        this._currIndex = this._currIndex % this._maxPosNums;
        this._currIndex = 0;
        let offset = this._currIndex * this._flaotsPerWave;
        this._centerOffsetTime[offset] = pos.x;
        this._centerOffsetTime[offset + 1] = pos.y;
        this._centerOffsetTime[offset + 2] = 0;
        this._centerOffsetTime[offset + 3] = this._waveTime;

        this._currIndex++;
    }

    public clear() {
        this._centerOffsetTime.fill(0);
        this._currIndex = 0;
    }

    private wave(time: number): number {
        let t = 1 - Math.max(Math.min(time, this._waveTime), 0) / this._waveTime;
        return this._strength * this.waveA * (Math.sin(this._waveW * t + this._waveP) + 1) * 0.5 * Math.exp(-this._attenuation * 20 * t);
    }
}
